/** @file SP3EphStore.hpp
 * Store a tabular list of position and clock bias (perhaps also
 * velocity and clock drift) data from SP3 file(s) for several
 * satellites; access the tables to compute values at any timetag,
 * within the limits of the data, from this table via interpolation.
 * An option allows assigning the clock store to RINEX clock files,
 * with separate timestep and interpolation algorithm. */

#ifndef SP3EphStore_INCLUDE
#define SP3EphStore_INCLUDE

#include <map>
#include <vector>
#include <algorithm>
#include <iostream>

#include "Exception.hpp"
#include "SatID.hpp"
#include "CommonTime.hpp"
#include "XvtStore.hpp"

#include "FileStore.hpp"
#include "ClockSatStore.hpp"
#include "PositionSatStore.hpp"

#include "SP3EphHeader.hpp"
#include "Rx3ClockHeader.hpp"

using namespace gpstk;

namespace gnssbox
{
      /** Store position and clock bias (and perhaps velocity and
       * drift) data from SP3 files, using (separate) stores based on
       * TabularSatStore. An option allows the clock store to be taken
       * from RINEX clock files instead.  Keep a FileStore for the SP3
       * input files, and for the RINEX clock files, if they are
       * used. Inherit XvtStore for the interface it defines. */
    class SP3EphStore : public XvtStore<SatID>
    {

         // member data
    private:

         /** Time system for this store. Must set, and keep
          * consistent, in loadFile.  Returned in getTimeSystem()
          * (virtual in XvtStore).
          * @note usually GPS, but CANNOT assume so. */
        TimeSystem storeTimeSystem;

         /// PositionSatStore for SP3 ephemeris data
        PositionSatStore posStore;

         /// ClockSatStore for SP3 OR RINEX clock data
        ClockSatStore clkStore;

         /// FileStore for the SP3 input files
        FileStore<SP3EphHeader> SP3Files;

         /// FileStore for the (optional) RINEX clock input files
        FileStore<Rx3ClockHeader> clkFiles;

         /** flag indicating whether the clock store contains data
          * from SP3 (true, the default) or Rinex clock (false)
          * files */
        bool useSP3clock;

         /** Flag to reject all data when there are bad positions,
          * default true. */
        bool rejectBadPosFlag;

         /** Flag to reject all data when there are bad clocks,
          * default true.
          * @note this flag has no effect if the clock store comes
          * from RINEX clock files. */
        bool rejectBadClockFlag;

         /** Flag to reject predicted position data, using
          * orbitPredFlag in SP3Data. */
        bool rejectPredPosFlag;

         /** Flag to reject predicted clock data, using clockPredFlag
          * in SP3Data.
          * @note this flag has no effect if the clock store comes
          * from RINEX clock files. */
        bool rejectPredClockFlag;

         // member functions

         /** Private utility routine used by the loadFile and
         * loadSP3File routines.  Store position (velocity) and clock
         * data from SP3 files in clock and position stores. Also
         * update the FileStore with the filename and SP3 header.
         * Check time systems consistentcy, and if possible set store
         * time system. */
        void loadSP3Store(const std::string& filename, bool fillClockStore)
            noexcept(false);

    public:

         /// Default constructor
        SP3EphStore() throw() : storeTimeSystem(TimeSystem::Any),
                                      useSP3clock(true),
                                      rejectBadPosFlag(true),
                                      rejectBadClockFlag(true),
                                      rejectPredPosFlag(false),
                                      rejectPredClockFlag(false)
        { }

         /// Destructor
        virtual ~SP3EphStore()
        { }

         // XvtStore interface:
         /** Returns the position, velocity, and clock offset of the
          * indicated object in ECEF coordinates (meters) at the
          * indicated time.
          * @param[in] sat the satellite of interest
          * @param[in] ttag the time to look up
          * @return the Xvt of the object at the indicated time
          * @throw InvalidRequest If the request can not be completed for any
          *    reason, this is thrown. The text may have additional
          *    information as to why the request failed. */
        virtual Xvt getXvt(const SatID& sat, const CommonTime& ttag)
            const noexcept(false);

         /** Dump information about the store to an ostream.
          * @param[in] os ostream to receive the output; defaults to std::cout
          * @param[in] detail integer level of detail to provide;
          *   allowed values are
          *    0: number of satellites, time step and time limits
          *    1: above plus flags, gap and interval values, and
          *       number of data/sat
          *    2: above plus all the data tables */
        virtual void dump(std::ostream& os = std::cout, short detail = 0)
            const throw()
        {
            // may have to re-write this...
            os << "Dump SP3EphStore:" << std::endl;
            // 'reject' flags
            os << (rejectBadPosFlag ? " Reject":" Do not reject")
               << " bad positions." << std::endl;
            os << (rejectBadClockFlag ? " Reject":" Do not reject")
               << " bad clocks." << std::endl;
            os << (rejectPredPosFlag ? " Reject":" Do not reject")
               << " predicted positions." << std::endl;
            os << (rejectPredClockFlag ? " Reject":" Do not reject")
               << " predicted clocks." << std::endl;

            SP3Files.dump(os, detail);
            posStore.dump(os, detail);
            if(!useSP3clock) clkFiles.dump(os, detail);
            clkStore.dump(os, detail);

            os << "End dump SP3EphStore." << std::endl;
        }

         /** Edit the dataset, removing data outside the indicated
          * time interval
          * @param[in] tmin defines the beginning of the time interval
          * @param[in] tmax defines the end of the time interval */
        virtual void edit(const CommonTime& tmin,
                        const CommonTime& tmax = CommonTime::END_OF_TIME) throw()
        {
            posStore.edit(tmin, tmax);
            clkStore.edit(tmin, tmax);
        }

         /// Clear the dataset, meaning remove all data
        virtual void clear(void) throw()
        { 
            clearPosition(); 
            clearClock(); 
        }

         /// Return time system (@note usually GPS, but CANNOT assume so)
        virtual TimeSystem getTimeSystem(void) const throw()
        { return storeTimeSystem; }

         /** Determine the earliest time for which this object can
          * successfully determine the Xvt for any object.
          * @return the earliest time in the table
          * @throw InvalidRequest if the object has no data. */
        virtual CommonTime getInitialTime() const noexcept(false);

         /** Determine the latest time for which this object can
          * successfully determine the Xvt for any object.
          * @return the latest time in the table
          * @throw InvalidRequest if the object has no data. */
        virtual CommonTime getFinalTime() const noexcept(false);

         /// Return true if IndexType=SatID is present in the data tables
        virtual bool isPresent(const SatID& sat) const throw()
        { return (posStore.isPresent(sat) && clkStore.isPresent(sat)); }

         /// Return true if velocity is present in the data tables
        virtual bool hasVelocity() const throw()
        {  return posStore.hasVelocity(); }

         // end of XvtStore interface

         /** Dump information about the position store to an ostream.
          * @param[in] os ostream to receive the output; defaults to std::cout
          * @param[in] detail integer level of detail to provide;
          *   allowed values are
          *    0: number of satellites, time step and time limits
          *    1: above plus flags, gap and interval values, and
          *       number of data/sat
          *    2: above plus all the data tables */
        void dumpPosition(std::ostream& os = std::cout, short detail = 0)
            const throw()
        {
            SP3Files.dump(os, detail);
            posStore.dump(os, detail);
        }

         /** Dump information about the clock store to an ostream.
          * @param[in] os ostream to receive the output; defaults to std::cout
          * @param[in] detail integer level of detail to provide;
          *   allowed values are
          *    0: number of satellites, time step and time limits
          *    1: above plus flags, gap and interval values, and
          *       number of data/sat
          *    2: above plus all the data tables */
        void dumpClock(std::ostream& os = std::cout, short detail = 0)
            const throw()
        {
            if(useSP3clock)
                SP3Files.dump(os, detail);
            else
                clkFiles.dump(os, detail);
            clkStore.dump(os, detail);
        }

         /** Return the position for the given satellite at the given time.
          * @param[in] sat the SatID of the satellite of interest
          * @param[in] ttag the time (CommonTime) of interest
          * @return Triple containing the position ECEF XYZ meters
          * @throw InvalidRequest if result cannot be computed, for
          *   example because:
          *  a) the time t does not lie within the time limits of the
          *     data table
          *  b) checkDataGap is true and there is a data gap
          *  c) checkInterval is true and the interval is larger than
          *     maxInterval */
        Triple getPosition(const SatID sat, const CommonTime ttag)
            const noexcept(false);

         /** Return the velocity for the given satellite at the given time
          * @param[in] sat the SatID of the satellite of interest
          * @param[in] ttag the time (CommonTime) of interest
          * @return Triple containing the velocity ECEF XYZ meters/second
          * @throw InvalidRequest if result cannot be computed, for
          *   example because
          *  a) the time t does not lie within the time limits of the
          *     data table
          *  b) checkDataGap is true and there is a data gap
          *  c) checkInterval is true and the interval is larger than
          *     maxInterval */
        Triple getVelocity(const SatID sat, const CommonTime ttag)
            const noexcept(false);

         /** Return the acceleration for the given satellite at the given time
          * @param[in] sat the SatID of the satellite of interest
          * @param[in] ttag the time (CommonTime) of interest
          * @return Triple containing the acceleration ECEF XYZ
          *   meters/second/second
          * @throw InvalidRequest if result cannot be computed, for
          *   example because
          *  a) the time t does not lie within the time limits of the
          *     data table
          *  b) checkDataGap is true and there is a data gap
          *  c) checkInterval is true and the interval is larger than
          *     maxInterval */
        Triple getAcceleration(const SatID sat, const CommonTime ttag)
            const noexcept(false)
        { return posStore.getAcceleration(sat,ttag); }


         /** Clear the position dataset only, meaning remove all data
          * from the tables. */
        virtual void clearPosition(void) throw()
        { 
            posStore.clear(); 
        }

         /** Clear the clock dataset only, meaning remove all data
          * from the tables. */
        virtual void clearClock(void) throw()
        { 
            clkStore.clear(); 
        }


         /** Choose to load the clock data tables from RINEX clock
          * files. This will clear the clock store; loadFile() or
          * loadRinexClockFile() should be called after this, to load
          * data into the clock store from RINEX clock files.  This
          * routine has no effect if the clock store is already set to
          * RINEX clock.
          * @note will be called by loadRinexClockFile() if clock
          * store is set to SP3. */
        void useRinexClockData(void) throw()
        {
            if(!useSP3clock) return;
            useSP3clock = false;
            clearClock();
        }

         /** Choose to load the clock data tables from SP3 files (this
         * is the default).  This will clear the clock store; if the
         * position store has already been loaded it should also be
         * cleared. The routines loadFile() or loadSP3File() should be
         * called after this, to load data into the clock store from
         * SP3 files.
         * @note this will also load position data into the position
         * store.  This routine has no effect if the clock store is
         * already set to SP3. */
        void useSP3ClockData(void) throw()
        {
            if(useSP3clock) return;
            useSP3clock = true;
            clearClock();
        }

         /** Get the earliest time of data in the position store.
          * @return CommonTime the first time
          * @throw InvalidRequest if there is no data */
        CommonTime getPositionInitialTime(void) const noexcept(false)
        { return posStore.getInitialTime(); }

         /** Get the latest time of data in the position store.
          * @return CommonTime the latest time
          * @throw InvalidRequest if there is no data */
        CommonTime getPositionFinalTime(void) const noexcept(false)
        { return posStore.getFinalTime(); }

         /** Get the earliest time of data in the clock store.
          * @return CommonTime the first time
          * @throw InvalidRequest if there is no data */
        CommonTime getClockInitialTime(void) const noexcept(false)
        { return clkStore.getInitialTime(); }

         /** Get the latest time of data in the clock store.
          * @return CommonTime the latest time
          * @throw InvalidRequest if there is no data */
        CommonTime getClockFinalTime(void) const noexcept(false)
        { return clkStore.getFinalTime(); }

         /** Get the earliest time of data in the position store for
          * the given satellite.
          * @return CommonTime the first time
          * @throw InvalidRequest if there is no data */
        CommonTime getPositionInitialTime(const SatID& sat) const
            noexcept(false)
        { return posStore.getInitialTime(sat); }

         /** Get the latest time of data in the position store for the
          * given satellite.
          * @return CommonTime the latest time
          * @throw InvalidRequest if there is no data */
        CommonTime getPositionFinalTime(const SatID& sat) const
            noexcept(false)
        { return posStore.getFinalTime(sat); }

         /** Get the earliest time of data in the clock store for the
          * given satellite.
          * @return CommonTime the first time
          * @throw InvalidRequest if there is no data */
        CommonTime getClockInitialTime(const SatID& sat) const
            noexcept(false)
        { return clkStore.getInitialTime(sat); }

         /** Get the latest time of data in the clock store for the
          * given satellite.
          * @return CommonTime the latest time
          * @throw InvalidRequest if there is no data */
        CommonTime getClockFinalTime(const SatID& sat) const
            noexcept(false)
        { return clkStore.getFinalTime(sat); }

         /** Get the earliest time of both clock and position data in
         * the store for the given satellite.
         * @return CommonTime the first time
         * @throw InvalidRequest if there is no data */
        CommonTime getInitialTime(const SatID& sat) const noexcept(false);

         /** Get the latest time of both clock and position data in
          * the store for the given satellite.
          * @return the latest time
          * @throw InvalidRequest if there is no data */
        CommonTime getFinalTime(const SatID& sat) const noexcept(false);


         /** Get the nominal time step in seconds for the position
          * data and the given sat */
        double getPositionTimeStep(const SatID& sat) const throw()
        { return posStore.nomTimeStep(sat); }

         /** Get the nominal time step in seconds for the clock data
          * and the given sat */
        double getClockTimeStep(const SatID& sat) const throw()
        { return clkStore.nomTimeStep(sat); }


         /// Get current interpolation order for the position table
        unsigned int getPositionInterpOrder(void) throw()
        { return posStore.getInterpolationOrder(); }

         /** Set the interpolation order for the position table; it is
          * forced to be even. */
        void setPositionInterpOrder(unsigned int order) throw()
        { posStore.setInterpolationOrder(order); }

         /** Get current interpolation order for the clock data
          * (meaningless if the interpolation type is linear). */
        unsigned int getClockInterpOrder(void) throw()
        { return clkStore.getInterpolationOrder(); }

         /** Set the interpolation order for the clock table; it is
          * forced to be even.  This is ignored if the clock
          * interpolation type is linear. */
        void setClockInterpOrder(unsigned int order) throw()
        { clkStore.setInterpolationOrder(order); }

         /** Set the type of clock interpolation to Lagrange (the
          * default); set the order of the interpolation with
          * setClockInterpolationOrder(order); */
        void setClockLagrangeInterp(void) throw()
        { clkStore.setLagrangeInterp(); }

         /** Set the type of clock interpolation to linear
          * (interpolation order is ignored). */
        void setClockLinearInterp(void) throw()
        { clkStore.setLinearInterp(); }


         /** Get a list (std::vector) of SatIDs present in both clock
          * and position stores */
        std::vector<SatID> getSatList(void) const throw()
        {
            std::vector<SatID> posList(posStore.getSatList());
            std::vector<SatID> clkList(clkStore.getSatList());
            std::vector<SatID> retList;
            for(size_t i=0; i<posList.size(); i++)
                if(std::find(clkList.begin(),clkList.end(),posList[i]) != clkList.end())
                    retList.push_back(posList[i]);
            return retList;
        }

         /// Get a list (std::vector) of SatIDs present in the position store
        std::vector<SatID> getPositionSatList(void) const throw()
        { return posStore.getSatList(); }

         /// Get a list (std::vector) of SatIDs present in the clock store
        std::vector<SatID> getClockSatList(void) const throw()
        { return clkStore.getSatList(); }


         /// Get the total number of (position) data records in the store
        inline int ndata(void) const throw()
        { return posStore.ndata(); }

         /// Get the number of (position) data records for the given sat
        inline int ndata(const SatID& sat) const throw()
        { return posStore.ndata(sat); }

         /** Get the number of (position) data records for the given
          * satellite system */
        inline int ndata(const SatelliteSystem::Systems& sys) const throw()
        { return posStore.ndata(sys); }

         /// Get the total number of position data records in the store
        inline int ndataPosition(void) const throw()
        { return posStore.ndata(); }

         /// Get the number of position data records for the given sat
        inline int ndataPosition(const SatID& sat) const throw()
        { return posStore.ndata(sat); }

         /** Get the number of position data records for the given
          * satellite system */
        inline int ndataPosition(const SatelliteSystem::Systems& sys) const throw()
        { return posStore.ndata(sys); }

         /// Get the total number of clock data records in the store
        inline int ndataClock(void) const throw()
        { return clkStore.ndata(); }

         /// Get the number of clock data records for the given sat
        inline int ndataClock(const SatID& sat) const throw()
        { return clkStore.ndata(sat); }

         /** Get the number of clock data records for the given
          * satellite system */
        inline int ndataClock(const SatelliteSystem::Systems& sys) const throw()
        { return clkStore.ndata(sys); }

         /// same as ndataPosition()
        inline int size(void) const throw() { return ndataPosition(); }


         /** Load an SP3 ephemeris file; may set the velocity and
          * acceleration flags.  If the clock store uses RINEX clock
          * data, this will ignore the clock data.
          * @param filename name of file (SP3 format) to load
          * @throw if time step is inconsistent with previous value */
        void loadSP3File(const std::string& filename) noexcept(false);

         /** Load a RINEX clock file; may set the 'have' bias and
          * drift flags.  If clock store is set to use SP3 data, this
          * will call useRinexClockData()
          * @param filename name of file (RINEX clock format) to load
          * @throw if time step is inconsistent with previous value */
        void loadRinexClockFile(const std::string& filename) noexcept(false);


         /** Add a complete PositionRecord to the store; this is the
          * preferred method of adding data to the tables.
          * @note If these addXXX() routines are used more than once
          * for the same record (sat,ttag), be aware that since ttag
          * is used as they key in a std::map, the value used must be
          * EXACTLY the same in all calls; (numerical noise could
          * cause the std::map to consider two "equal" ttags as
          * different). */
        void addPositionRecord(const SatID& sat, const CommonTime& ttag,
                               const PositionRecord& data) noexcept(false)
        {
            try { posStore.addPositionRecord(sat,ttag,data); }
            catch(InvalidRequest& ir) { GPSTK_RETHROW(ir); }
        }

         /// Add position data to the store
        void addPositionData(const SatID& sat, const CommonTime& ttag,
                             const Triple& Pos, const Triple& sig) noexcept(false)
        {
            try { posStore.addPositionData(sat,ttag,Pos,sig); }
            catch(InvalidRequest& ir) { GPSTK_RETHROW(ir); }
        }

         /// Add velocity data to the store
        void addVelocityData(const SatID& sat, const CommonTime& ttag,
                             const Triple& Vel, const Triple& sig) noexcept(false)
        {
            try { posStore.addVelocityData(sat,ttag,Vel,sig); }
            catch(InvalidRequest& ir) { GPSTK_RETHROW(ir); }
        }

         /** Add a complete ClockRecord to the store; this is the
          * preferred method of adding data to the tables.
          * @note If these addXXX() routines are used more than once
          * for the same record (sat,ttag), be aware that since ttag
          * is used as they key in a std::map, the value used must be
          * EXACTLY the same in all calls; (numerical noise could
          * cause the std::map to consider two "equal" ttags as
          * different). */
        void addClockRecord(const SatID& sat, const CommonTime& ttag,
                            const ClockRecord& rec)
            noexcept(false)
        {
            try { clkStore.addClockRecord(sat,ttag,rec); }
            catch(InvalidRequest& ir) { GPSTK_RETHROW(ir); }
        }

         /// Add clock bias data (only) to the store
        void addClockBias(const SatID& sat, const CommonTime& ttag,
                          const double& bias, const double& sig=0.0)
            noexcept(false)
        {
            try { clkStore.addClockBias(sat,ttag,bias,sig); }
            catch(InvalidRequest& ir) { GPSTK_RETHROW(ir); }
        }

         /// Add clock drift data (only) to the store
        void addClockDrift(const SatID& sat, const CommonTime& ttag,
                           const double& drift, const double& sig=0.0)
            noexcept(false)
        {
            try { clkStore.addClockDrift(sat,ttag,drift,sig); }
            catch(InvalidRequest& ir) { GPSTK_RETHROW(ir); }
        }

         /// Add clock acceleration data (only) to the store
        void addClockAcceleration(const SatID& sat, const CommonTime& ttag,
                                  const double& accel, const double& sig=0.0)
            noexcept(false)
        {
            try { clkStore.addClockAcceleration(sat,ttag,accel,sig); }
            catch(InvalidRequest& ir) { GPSTK_RETHROW(ir); }
        }


         /// Get number of files (all types) in FileStore.
        int nfiles(void) throw()
        { return (SP3Files.size() + (useSP3clock ? 0 : clkFiles.size())); }

         /// Get number of SP3 files in FileStore.
        int nSP3files(void) throw()
        { return SP3Files.size(); }

         /// Get number of clock files in FileStore.
        int nClockfiles(void) throw()
        { return (useSP3clock ? SP3Files.size() : clkFiles.size()); }


         /// Return true if there is drift data in the tables
        virtual bool hasClockDrift() const throw()
        { return clkStore.hasClockDrift(); }


         /** Set the flag; if true then all values are rejected when a
          * bad position value is found, while adding data to the
          * store. */
        void rejectBadPositions(const bool flag)
        { rejectBadPosFlag = flag; }

         /** Set the flag; if true then all values are rejected when bad clock
          * values are found, while adding data to the store. */
        void rejectBadClocks(const bool flag)
        { rejectBadClockFlag = flag; }

         /** Set the flag; if true then predicted position values are
          * rejected when adding data to the store. */
        void rejectPredPositions(const bool flag)
        { rejectPredPosFlag = flag; }

         /** Set the flag; if true then predicted clock values are
          * rejected when adding data to the store. */
        void rejectPredClocks(const bool flag)
        { rejectPredClockFlag = flag; }


         /// Is gap checking for position on?
        bool isPosDataGapCheck(void) throw()
        { return posStore.isDataGapCheck(); }

         /// Is gap checking for clock on?
        bool isClkDataGapCheck(void) throw()
        { return clkStore.isDataGapCheck(); }

         /// Disable checking of data gaps in both position and clock.
        void disableDataGapCheck(void) throw()
        { posStore.disableDataGapCheck(); clkStore.disableDataGapCheck(); }

         /// Disable checking of data gaps in position store
        void disablePosDataGapCheck(void) throw()
        { posStore.disableDataGapCheck(); }

         /// Disable checking of data gaps in clock store
        void disableClockDataGapCheck(void) throw()
        { clkStore.disableDataGapCheck(); }

         /// Get current gap interval in the position store.
        double getPosGapInterval(void) throw()
        { return posStore.getGapInterval(); }

         /// Get current gap interval in the clock store.
        double getClockGapInterval(void) throw()
        { return clkStore.getGapInterval(); }

         /** Set gap interval and turn on gap checking in the position
          * store. There is no default. */
        void setPosGapInterval(double interval) throw()
        { posStore.setGapInterval(interval); }

         /** Set gap interval and turn on gap checking in the clock
          * store.  There is no default. */
        void setClockGapInterval(double interval) throw()
        { clkStore.setGapInterval(interval); }


         /// Is interval checking for position on?
        bool isPosIntervalCheck(void) throw()
        { return posStore.isIntervalCheck(); }

         /// Is interval checking for clock on?
        bool isClkIntervalCheck(void) throw()
        { return clkStore.isIntervalCheck(); }

         /// Disable checking of maximum interval in both position and clock.
        void disableIntervalCheck(void) throw()
        { posStore.disableIntervalCheck(); clkStore.disableIntervalCheck(); }

         /// Disable checking of maximum interval in position store
        void disablePosIntervalCheck(void) throw()
        { posStore.disableIntervalCheck(); }

         /// Disable checking of maximum interval in clock store
        void disableClockIntervalCheck(void) throw()
        { clkStore.disableIntervalCheck(); }

         /// Get current maximum interval in the position store
        double getPosMaxInterval(void) throw()
        { return posStore.getMaxInterval(); }

         /// Get current maximum interval in the clock store
        double getClockMaxInterval(void) throw()
        { return clkStore.getMaxInterval(); }

         /** Set maximum interval and turn on interval checking in the
          * position store There is no default. */
        void setPosMaxInterval(double interval) throw()
        { posStore.setMaxInterval(interval); }

         /** Set maximum interval and turn on interval checking in the
          * clock store There is no default. */
        void setClockMaxInterval(double interval) throw()
        { clkStore.setMaxInterval(interval); }


         /// @deprecated
        virtual bool velocityIsPresent() const throw()
        { return posStore.hasVelocity(); }

         /// @deprecated
        virtual bool clockIsPresent() const throw()
        { return true; }

         /** @deprecated
          * Get current (position) interpolation order */
        unsigned int getInterpolationOrder(void) throw()
        { return getPositionInterpOrder(); }

         /** @deprecated
          * Set the (position) interpolation order for the position table;
          * it is forced to be even. */
        void setInterpolationOrder(unsigned int order) throw()
        { setPositionInterpOrder(order); }

    }; // end class SP3EphStore


}  // End of namespace gnssbox

#endif // SP3EphStore_INCLUDE
